import java.util.ArrayList;

/**
 * @author Anna-Katharina Wickert 
 * @author Yannic Remmet
 * @author Tobias Trabelsi
 * 
 * @version 0.8.1
 * @since 22.06.2012
 *
 * @license Beer-Ware License (Revision 42)
 *
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * We wrote this file. As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy us a beer in return.
 * ----------------------------------------------------------------------------
 */
/**
 * 
 * @author yannicremmet
 *	Game of Life Logic 
 */
public class Logic implements Runnable {

	
	/* Runnable */
	@Override
	public void run() {
		while(c != null && c.getRun() && nextGeneration()){
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
	
	/* Observer */
	ArrayList<MyObserver> observer = new ArrayList<MyObserver>();
	/**
	 * Updates all registered Observers 
	 */
	protected synchronized void setChanged() {
		for(MyObserver o : observer)
			o.update(changes);
	}
	/**
	 * Adds Observer
	 * @param o new Observer
	 */
	public synchronized void addObserver(MyObserver o) {
		observer.add(o);
	}
	
	
	
	/* Own Code */
	private int[][] cells;
	private int width;
	private int height;
	private int generation;
	private Controlls c = null;
	private ArrayList<Integer> changes = new ArrayList<Integer>();
	
	public Logic() {
		this(50,50);
	}
	public Logic(int i, int j) {
		width = i;
		height = j;
		cells = new int[width][height];

		for( i=0; i<width; i++)
			for( j = 0; j<height; j++)
					cells[i][j] = 0;
		generation =0;
	}
	/**
	 * Set Control Interface
	 * @param c the Control Class 
	 */
	public void setC(Controlls c) {
		this.c = c;
	}
	/**
	 * Sets cell allive
	 * @param x x-Axis Coordinate
	 * @param y y-Axis Coordinate
	 */
	private void givingBirth(int x, int y){
		for (int i=x-1;i<=x+1;i++)
            for(int j=y-1;j<=y+1;j++)
                if (i>=0 && j>=0 && i<width && j<height ){
                	if (i == x && j == y){
                		cells[i][j] += 10;
    					this.changes.add(i);
    					this.changes.add(j);
                	}
                	else{
                		cells[i][j]++;
                	}
                }
		setChanged();
	}
	/**
	 * Sets cell dead
	 * @param x x-Axis Coordinate
	 * @param y y-Axis Coordinate
	 */
	private void killCell(int x, int y){
		for (int i=x-1;i<=x+1;i++)
            for(int j=y-1;j<=y+1;j++)
                if (i>=0 && j>=0 && i<width && j<height ){
                	if (i == x && j == y){
                		cells[i][j] -= 10;
    					this.changes.add(i);
    					this.changes.add(j);
                	}
                	else{
                		cells[i][j]--;
                	}
                }
		setChanged();
	}
	/**
	 * do next generation
	 * @return true if something changed
	 */
	public boolean nextGeneration(){
		if(checkBorderRule())
			return false;
		int[][] copy = new int[width][height];
		for(int i=0; i<width; i++)
			for(int j = 0; j<height; j++)
				copy[i][j] = cells[i][j];
		
		boolean changes = false;
		for(int i=0; i<width; i++)
			for(int j = 0; j<height; j++){
				if (copy[i][j] == 3){
					givingBirth(i, j);
					changes = true;
					this.changes.add(i);
					this.changes.add(j);
				}
				if (copy[i][j] > 13 || copy[i][j] == 11 || copy[i][j] == 10){
					killCell(i, j);
					changes = true;
					this.changes.add(i);
					this.changes.add(j);
				}		
			}
		if(changes){
			generation++;
			setChanged();
		}
		return changes;
	}
	/**
	 * Sets all cells death
	 */
	public void clear(){
		for(int i =0 ; i< width; i++)
			for(int j= 0;j<height ; j++){
				cells[i][j]=0;
				changes.add(i);
				changes.add(j);
			}
		setChanged();
		
	}
	/**
	 * Check the borderrule
	 * @return true if a cell trespass the field 
	 */
	private boolean checkBorderRule() {
		int change = 0;
		for (int i =0; i< width; i++){
			if (isAlive(i, 0))
				change++;
			else
				change = 0;
			if (change == 3)
				return true;
		}
		change =0;
		for (int i =0; i< width; i++){
			if (isAlive(i, height-1))
				change++;
			else
				change = 0;
			if (change == 3)
				return true;
		}
		change =0;
		for (int i =0; i< height-1; i++){
			if (isAlive(0, i))
				change++;
			else
				change = 0;
			if (change == 3)
				return true;
		}
		change =0;
		for (int i =0; i< height; i++){
			if (isAlive(width-1, i))
				change++;
			else
				change = 0;
			if (change == 3)
				return true;
		}
		return false;
	}
	/**
	 * Retrurs ture if the Cell is allive
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean isAlive(int x, int y) {
		return (cells[x][y] >9) ? true : false;
	}
	public int getValue(int x, int y){
		return cells[x][y];
	}
	public int getWidth() {
		return width;
	}
	public int getHeight() {
		return height;
	}
	public int getGeneration() {
		return generation;
	}
	public void cellChangeState(int i, int j) {
		if (isAlive(i, j))
			killCell(i, j);
		else
			givingBirth(i, j);
	}
	public String toString(){
		StringBuilder sb = new StringBuilder();
		for (int j = 0; j < height; j++){
			for(int i = 0; i < width; i++){
				sb.append((cells[i][j] > 9) ? cells[i][j] + " " : " "+cells[i][j]+" ");
			}
			sb.append("\n");
		}
		return sb.toString();
	}
	
	public void pattern(int index, int x ,int y){
		if(index ==1){
			givingBirth(x, y-1);
			givingBirth(x, y);
			givingBirth(x, y+1);
		}
		if(index ==2){
			givingBirth(x, y-2);
			givingBirth(x, y-1);
			givingBirth(x-1, y);
			givingBirth(x, y);
			givingBirth(x+1, y);
			givingBirth(x, y+1);
		}
		if(index ==3){
			givingBirth(x-1, y-1);
			givingBirth(x-1, y);
			givingBirth(x-1, y+1);
			givingBirth(x+1, y-1);
			givingBirth(x+1, y);
			givingBirth(x+1, y+1);
			givingBirth(x, y);
		}
		if(index ==4){
			givingBirth(x, y);
			givingBirth(x-1, y);
			givingBirth(x, y-1);
			givingBirth(x, y+1);
			givingBirth(x+1, y-1);
		}
		if(index ==5){
			givingBirth(x+1, y-1);
			givingBirth(x-1, y);
			givingBirth(x, y);
			givingBirth(x+1, y+1);
			givingBirth(x+2, y+1);
			givingBirth(x, y+2);
		}
		if(index ==6){
			givingBirth(x-1, y-1);
			givingBirth(x-2, y);
			givingBirth(x-2, y+1);
			givingBirth(x+2, y+1);
			givingBirth(x-2, y+2);
			givingBirth(x-1, y+2);
			givingBirth(x  , y+2);
			givingBirth(x+1, y+2);
		}
		if(index == 7){
			givingBirth(x, y-1);
			givingBirth(x-1, y);
			givingBirth(x-1, y+1);
			givingBirth(x, y+1);
			givingBirth(x+1, y+1);
		}
		if(index ==8){
			givingBirth(x-1, y);
			givingBirth(x, y);
			givingBirth(x+1, y);
			givingBirth(x	, y-1);
			givingBirth(x+1, y-1);
			givingBirth(x+2, y-1);
		}
		if(index ==9){
			givingBirth(x-1,y-2);
			givingBirth(x,y-2);
			givingBirth(x+1,y-2);
			givingBirth(x-2,y-1);
			givingBirth(x+2,y-1);
			givingBirth(x-2,y);
			givingBirth(x+2,y);
			givingBirth(x-2,y+1);
			givingBirth(x+2,y+1);
			givingBirth(x-1,y+2);
			givingBirth(x,y+2);
			givingBirth(x+1,y+2);
		}
		if(index ==10){
			givingBirth(x-1,y-1);
			givingBirth(x,y-1);
			givingBirth(x-1,y);
			givingBirth(x,y);
			givingBirth(x+1,y+1);
			givingBirth(x+2,y+1);
			givingBirth(x+1,y+2);
			givingBirth(x+2,y+2);
		}
		
	}
	public final String[] pattern = {"Standard",
			 "Blinker",				//1
			 "Latin Cross",			//2
			 "H",					//3
			 "r-Pentamino",			//4
			 "Clock",				//5
			 "Leichter Segler",		//6
			 "Gleiter",				//7
			 "Kroete",				//8
			 "O",					//9
			 "Beacon"};				//10
}
